#include "StdAfx.h"
#include "AngleRect.h"


CAngleRect::CAngleRect(double x, double y, double width, double height,
  double angle)
{
  angle_rect_.x       = x;
  angle_rect_.y       = y;
  angle_rect_.width   = width;
  angle_rect_.height  = height;
  angle_rect_.angle   = angle;

  hit_code_ = kHitNothing;

  Rotate();
  AdjustHandles();
}

CAngleRect::~CAngleRect(void)
{
}

void CAngleRect::LeftButtonDown_slot(CWnd* sender, const MouseArgument& arg)
{
  if (!visible_) {
    return;
  }

  if (CursorOn(arg.point)) {
    HitTest(arg.point);

    tracking_	= true;
    anchor_	= arg.point;
    sender->SetCapture();

    if (!selected_) {
      sender->Invalidate(FALSE);
    }

    selected_ = true;
  } else {
    if(selected_) {
      sender->Invalidate(FALSE);
      selected_ = false;
    }
  }
}

void CAngleRect::LeftButtonUp_slot(CWnd* sender, const MouseArgument& arg)
{
  if(tracking_) {
    tracking_	= false;
    AdjustHandles();

    if(sender->GetCapture() == sender) {
      ::ReleaseCapture();
    }

    sender->Invalidate(FALSE);
  }
}

void CAngleRect::MouseMove_slot(CWnd* sender, const MouseArgument& arg)
{
  if(tracking_) {
    TrackTo(arg.point);
    AdjustHandles();

    sender->Invalidate(FALSE);
  }
}

bool CAngleRect::CursorSet_slot(CWnd* sender, const MouseArgument& arg)
{
  if (!visible_) {
    return false;
  }

  if(arg.flags!=HTCLIENT) {
    return false;
  }

  HitTest(arg.point);

  switch(hit_code_)
  {
  case kHitEast:
    ::SetCursor(kBorderCursors[Quadrant(angle_rect_.angle)]);
    return true;

  case kHitNorthEast:
    ::SetCursor(kBorderCursors[(Quadrant(angle_rect_.angle)+1)%8]);
    return true;

  case kHitNorth:
    ::SetCursor(kBorderCursors[(Quadrant(angle_rect_.angle)+2)%8]);
    return true;

  case kHitNorthWest:
    ::SetCursor(kBorderCursors[(Quadrant(angle_rect_.angle)+3)%8]);
    return true;

  case kHitWest:
    ::SetCursor(kBorderCursors[Quadrant(angle_rect_.angle)]);
    return true;

  case kHitSouthWest:
    ::SetCursor(kBorderCursors[(Quadrant(angle_rect_.angle)+1)%8]);
    return true;

  case kHitSouth:
    ::SetCursor(kBorderCursors[(Quadrant(angle_rect_.angle)+2)%8]);
    return true;

  case kHitSouthEast:
    ::SetCursor(kBorderCursors[(Quadrant(angle_rect_.angle)+3)%8]);
    return true;

  case kHitTop:
  case kHitBottom:
    ::SetCursor(kBorderCursors[Quadrant(angle_rect_.angle)]);
    return true;

  case kHitLeft:
  case kHitRight:
    ::SetCursor(kBorderCursors[(Quadrant(angle_rect_.angle)+2)%8]);
    return true;

  case kHitMiddle:
    ::SetCursor(kSizeAll);
    return true;

  default : return false;
  }
}

void CAngleRect::Draw(Hlong halcon_wnd_handle)
{
  if (!visible_) {
    return;
  }

  Halcon::set_draw(halcon_wnd_handle, "margin");
  Halcon::set_line_width(halcon_wnd_handle, kBorderWidth);

  if (selected_) {
    Halcon::set_rgb(halcon_wnd_handle, GetRValue(handle_color_),
      GetGValue(handle_color_), GetBValue(handle_color_));

    Halcon::disp_line(halcon_wnd_handle, handles_[kQuadrantI].y(),
      handles_[kQuadrantI].x(), handles_[kQuadrantV].y(),
      handles_[kQuadrantV].x());
    Halcon::disp_line(halcon_wnd_handle, handles_[kQuadrantIII].y(),
      handles_[kQuadrantIII].x(), handles_[kQuadrantVII].y(),
      handles_[kQuadrantVII].x());

    for (int i=0; i<8; ++i) {
      Halcon::disp_circle(halcon_wnd_handle, handles_[i].y(), handles_[i].x(),
        handles_[i].width()/2);
    }
  }

  Halcon::set_rgb(halcon_wnd_handle, GetRValue(border_color_),
    GetGValue(border_color_), GetBValue(border_color_));

  Halcon::disp_rectangle2(halcon_wnd_handle, angle_rect_.y, angle_rect_.x,
    RADIAN(angle_rect_.angle), angle_rect_.width/2, angle_rect_.height/2);

  geometry::Point2D point = 0.8*vertexes_[3] + 0.2*vertexes_[0];
  Halcon::disp_arrow(halcon_wnd_handle, vertexes_[0].y(), vertexes_[0].x(),
    point.y(), point.x(), kArrowHeight*0.75);

  point = 0.8*vertexes_[2] + 0.2*vertexes_[1];
  Halcon::disp_arrow(halcon_wnd_handle, vertexes_[1].y(), vertexes_[1].x(),
    point.y(), point.x(), kArrowHeight*0.75);
}

double CAngleRect::TrackAll(const geometry::Point2D& vertex0,
  const geometry::Point2D& vertex1, double length0,
  geometry::Point2D& vertex3, geometry::Point2D& vertex2,
  double length1, const geometry::Point2D& pt)
{
  geometry::Point2D vec1(vertex3.x()-vertex0.x(), vertex3.y()-vertex0.y());
  geometry::Point2D vec2(pt.x()-vertex0.x(), pt.y()-vertex0.y());
  double dist = length1;

  if (IsAcute(vec1, vec2)) {
    dist = Distance(vertex0, vertex1, length0, pt);
    geometry::Point2D	Vl(dist, dist);
    double t	= 1.0;

    if(length1 != 0.0) {
      t	= dist/length1;
      Vl	= vec1;
    } else if(length0 != 0.0) {
      t		= dist/length0;
      Vl.set_x(vertex1.y()-vertex0.y());
      Vl.set_y(vertex0.x()-vertex1.x());
    }

    vertex3.set_x(vertex0.x()+t*Vl.x());
    vertex3.set_y(vertex0.y()+t*Vl.y());
    vertex2.set_x(vertex1.x()+t*Vl.x());
    vertex2.set_y(vertex1.y()+t*Vl.y());
  }

  return dist;
}

void CAngleRect::Rotate(void)
{
}

void CAngleRect::AdjustHandles(void)
{
  geometry::Point2D effectZone[4];
  effectZone[0] = vertexes_[0];
  effectZone[1] = vertexes_[1];
  effectZone[2] = vertexes_[2];
  effectZone[3] = vertexes_[3];

  InflateRect(effectZone, angle_rect_.width, angle_rect_.height,
    kHandleSize*1.5f, kHandleSize*1.5f);

  geometry::Point2D mid;

  //******************Right*******************
	mid.set_x((effectZone[2].x()+effectZone[3].x())/2);
	mid.set_y((effectZone[2].y()+effectZone[3].y())/2);
	handles_[kQuadrantI] = geometry::Rect2D(mid, kHandleSize);

  //****************NorthEast****************
	handles_[kQuadrantII] = geometry::Rect2D(vertexes_[3], kHandleSize);

	//******************Top******************
	mid.set_x((effectZone[0].x()+effectZone[3].x())/2);
	mid.set_y((effectZone[0].y()+effectZone[3].y())/2);
	handles_[kQuadrantIII] = geometry::Rect2D(mid, kHandleSize);

	//****************NorthWest****************
	handles_[kQuadrantIV] = geometry::Rect2D(vertexes_[0], kHandleSize);

  	//******************Left*******************
	mid.set_x((effectZone[0].x()+effectZone[1].x())/2);
	mid.set_y((effectZone[0].y()+effectZone[1].y())/2);
	handles_[kQuadrantV] = geometry::Rect2D(mid, kHandleSize);

	//****************SouthWest****************
	handles_[kQuadrantVI] = geometry::Rect2D(vertexes_[1], kHandleSize);

	//******************Bottom******************
	mid.set_x((effectZone[1].x()+effectZone[2].x())/2);
	mid.set_y((effectZone[1].y()+effectZone[2].y())/2);
	handles_[kQuadrantVII] = geometry::Rect2D(mid, kHandleSize);

	//****************SouthEast****************
	handles_[kQuadrantVIII] = geometry::Rect2D(vertexes_[2], kHandleSize);

	//****************BoundingBox****************
	bounding_box_.set_left(vertexes_[0].x());
	bounding_box_.set_top(vertexes_[0].y());
	bounding_box_.set_right(vertexes_[0].x());
	bounding_box_.set_bottom(vertexes_[0].y());
	for(int i=1; i<4; ++i) {
		if(vertexes_[i].x()<bounding_box_.left<double>()) {
      bounding_box_.set_left(vertexes_[i].x());
    }

		if(vertexes_[i].y()<bounding_box_.top<double>()) {
      bounding_box_.set_top(vertexes_[i].y());
    }

		if(vertexes_[i].x()>bounding_box_.right<double>()) {
      bounding_box_.set_right(vertexes_[i].x());
    }

		if(vertexes_[i].y()>bounding_box_.bottom<double>()) {
      bounding_box_.set_bottom(vertexes_[i].y());
    }
	}
}

bool CAngleRect::CursorOn(const geometry::Point2D &point) const
{
  if (selected_) {
    for(int i=0; i<8; ++i) {
      if(handles_[i].PtInRect(point)) {
        return true;
      }
    }

    geometry::Segment2D segment_vertical(handles_[kQuadrantIII].center(),
      handles_[kQuadrantVII].center());
    if (segment_vertical.PtNear(point, kTolerance)) {
      return true;
    }

    geometry::Segment2D segment_horizontal(handles_[kQuadrantV].center(),
      handles_[kQuadrantI].center());
    if (segment_horizontal.PtNear(point, kTolerance)) {
      return true;
    }
  }

  return PtOnAngleRect(point);
}

bool CAngleRect::PtInside(const geometry::Point2D &pt) const
{
  geometry::Point2D vtx[4];
  vtx[0] = vertexes_[0];
  vtx[1] = vertexes_[1];
  vtx[2] = vertexes_[2];
  vtx[3] = vertexes_[3];

  bool b0 = ((vtx[0].y()-vtx[1].y())*pt.x()+(vtx[1].x()-vtx[0].x())*pt.y()+
    (vtx[0].x()*vtx[1].y()-vtx[1].x()*vtx[0].y()))*
    ((vtx[3].y()-vtx[2].y())*pt.x()+(vtx[2].x()-vtx[3].x())*pt.y()+
    (vtx[3].x()*vtx[2].y()-vtx[2].x()*vtx[3].y()))<0;

  bool b1 = ((vtx[0].y()-vtx[3].y())*pt.x()+(vtx[3].x()-vtx[0].x())*pt.y()+
    (vtx[0].x()*vtx[3].y()-vtx[3].x()*vtx[0].y()))*
    ((vtx[1].y()-vtx[2].y())*pt.x()+(vtx[2].x()-vtx[1].x())*pt.y()+
    (vtx[1].x()*vtx[2].y()-vtx[2].x()*vtx[1].y()))<0;

  return b0 && b1;
}

bool CAngleRect::PtOnAngleRect(const geometry::Point2D &point) const
{
  return PtInside(point) ||
    PtNearSegment(vertexes_[0], vertexes_[1], point) ||
    PtNearSegment(vertexes_[1], vertexes_[2], point) ||
    PtNearSegment(vertexes_[2], vertexes_[3], point) ||
    PtNearSegment(vertexes_[3], vertexes_[0], point);
}

void CAngleRect::HitTest(const geometry::Point2D& point)
{
  hit_code_ = kHitNothing;

  // Test if mouse hit NorthEast handle
  if(handles_[1].PtInRect(point)) {
    hit_code_ = kHitNorthEast; return;
  }

  // Test if mouse hit NorthWest handle
  if(handles_[3].PtInRect(point)) {
    hit_code_ = kHitNorthWest; return;
  }

  // Test if mouse hit SouthWest handle
  if(handles_[5].PtInRect(point)) {
    hit_code_ = kHitSouthWest; return;
  }

  // Test if mouse hit SouthEast handle
  if(handles_[7].PtInRect(point)) {
    hit_code_ = kHitSouthEast; return;
  }

  // Test if mouse hit East border
  if(PtNearSegment(vertexes_[2], vertexes_[3], point)) {
    hit_code_ = kHitEast; return;
  }

  // Test if mouse hit North border
  if(PtNearSegment(vertexes_[0], vertexes_[3], point)) {
    hit_code_ = kHitNorth; return;
  }

  // Test if mouse hit West border
  if(PtNearSegment(vertexes_[0], vertexes_[1], point)) {
    hit_code_ = kHitWest; return;
  }

  // Test if mouse hit South border
  if(PtNearSegment(vertexes_[1], vertexes_[2], point)) {
    hit_code_ = kHitSouth; return;
  }

  // Test if mouse hit center area
  geometry::Point2D ptCen(angle_rect_.x, angle_rect_.y);
  double rCen = min(kTolerance, min(angle_rect_.width, angle_rect_.height));
  if(geometry::square(point.x()-ptCen.x())+
    geometry::square(point.y()-ptCen.y()) < geometry::square(rCen)) {
    hit_code_ = kHitMiddle; return;
  }

  if(selected_) {
    // Test if mouse hit Right handle
    if(PtNearSegment(handles_[0].center(), ptCen, point) ||
      handles_[0].PtInRect(point)) {
      hit_code_ = kHitRight; return;
    }

    // Test if mouse hit Top handle
    if(PtNearSegment(handles_[2].center(), ptCen, point) ||
      handles_[2].PtInRect(point)) {
      hit_code_ = kHitTop; return;
    }

    // Test if mouse hit Left handle
    if(PtNearSegment(handles_[4].center(), ptCen, point) ||
      handles_[4].PtInRect(point)) {
      hit_code_ = kHitLeft; return;
    }

    // Test if mouse hit Bottom handle
    if(PtNearSegment(handles_[6].center(), ptCen, point) ||
      handles_[6].PtInRect(point)) {
      hit_code_ = kHitBottom; return;
    }
  }

  // Test if mouse hit rectangle interior
  if(PtInside(point)) {
    hit_code_ = kHitMiddle; return;
  }
}

void CAngleRect::TrackTo(const geometry::Point2D& point)
{
  switch(hit_code_)
  {
  case kHitEast:
    TrackEast(point);
    angle_rect_.x = (vertexes_[0].x()+vertexes_[2].x())/2;
    angle_rect_.y = (vertexes_[0].y()+vertexes_[2].y())/2;
    break;

  case kHitNorthEast:
    TrackNorth(point);
    TrackEast(point);
    angle_rect_.x = (vertexes_[0].x()+vertexes_[2].x())/2;
    angle_rect_.y = (vertexes_[0].y()+vertexes_[2].y())/2;
    break;

  case kHitNorth:
    TrackNorth(point);
    angle_rect_.x = (vertexes_[0].x()+vertexes_[2].x())/2;
    angle_rect_.y = (vertexes_[0].y()+vertexes_[2].y())/2;
    break;

  case kHitNorthWest:
    TrackNorth(point);
    TrackWest(point);
    angle_rect_.x = (vertexes_[0].x()+vertexes_[2].x())/2;
    angle_rect_.y = (vertexes_[0].y()+vertexes_[2].y())/2;
    break;

  case kHitWest:
    TrackWest(point);
    angle_rect_.x = (vertexes_[0].x()+vertexes_[2].x())/2;
    angle_rect_.y = (vertexes_[0].y()+vertexes_[2].y())/2;
    break;

  case kHitSouthWest:
    TrackSouth(point);
    TrackWest(point);
    angle_rect_.x = (vertexes_[0].x()+vertexes_[2].x())/2;
    angle_rect_.y = (vertexes_[0].y()+vertexes_[2].y())/2;
    break;

  case kHitSouth:
    TrackSouth(point);
    angle_rect_.x = (vertexes_[0].x()+vertexes_[2].x())/2;
    angle_rect_.y = (vertexes_[0].y()+vertexes_[2].y())/2;
    break;

  case kHitSouthEast:
    TrackSouth(point);
    TrackEast(point);
    angle_rect_.x = (vertexes_[0].x()+vertexes_[2].x())/2;
    angle_rect_.y = (vertexes_[0].y()+vertexes_[2].y())/2;
    break;

  case kHitMiddle:
    {
      geometry::Point2D delt(point.x()-anchor_.x(), point.y()-anchor_.y());
      angle_rect_.x += delt.x();
      angle_rect_.y += delt.y();
      Rotate();
    }
    break;

  default : break;
  }

  anchor_	= point;
}
